package model;

import gameAppLayer.enums.Constants;

import java.util.ArrayList;

import stars.StarModel;
import utilities.Key;

import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;

/*
 * A PhysicalDataset object holds mass, energy, volume, temp, etc. information for objects that have a full physical presence in-game.
 * 
 * This container object holds one or more PhysicalDatasets and provides aggregate data on them: Summations, averages, etc. of their characteristics.
 * Any queries to the container return these aggregate properties, while queries to the individual objects in the container return their specific properties
 */
public class PhysicalDataModelContainer implements IPhysicalDataModelContainer {

	protected double mass;  //kg	
	protected double volume;  //m3	
	protected double density;  //kg/m3	
	protected double fullEnergy;  //Unsure of units at this time.  Exajoules?	
	//energy sub-types
	protected double massEnergy; //energy to be gained from direct conversion	
	protected double spinEnergy; //energy that can be extracted from spin	
	protected double radiationEnergy; //energy that can be extracted from solar radiation/light	
	protected double gravEnergy; //energy that can be extracted from gravity well	
	protected double magneticEnergy; //energy that can be extracted from magnetic field	
	
	protected Vector2f location = new Vector2f().zero();
	protected Vector2f velocity = new Vector2f().zero();
	
	protected double age;  //yrs		
	protected double gravity;  //G's	
	protected double rotation;  //rotations/sec		
	protected double temperature;  //K	
	protected double radius;  //km	
	protected double orbitalRadius;  //km	
	protected double luminosity;  //W	
	protected double magField;  //Gauss	
	
	//game characteristics
	protected ArrayList<Key> gameEventList = new ArrayList<Key>();
	protected ArrayList<Key> cacheList = new ArrayList<Key>();
	protected ArrayList<Key> itemList = new ArrayList<Key>();
	protected ArrayList<Key> puzzleList = new ArrayList<Key>();
	
	protected ArrayList<Double> massList = new ArrayList<Double>();
	protected ArrayList<Double> volumeList = new ArrayList<Double>();
	protected ArrayList<Double> densityList = new ArrayList<Double>();
	protected ArrayList<Double> fullEnergyList = new ArrayList<Double>();
	protected ArrayList<Double> massEnergyList = new ArrayList<Double>();
	protected ArrayList<Double> spinEnergyList = new ArrayList<Double>();
	protected ArrayList<Double> radiationEnergyList = new ArrayList<Double>();
	protected ArrayList<Double> gravEnergyList = new ArrayList<Double>();
	protected ArrayList<Double> magneticEnergyList = new ArrayList<Double>();
	protected ArrayList<Double> ageList = new ArrayList<Double>();
	protected ArrayList<Double> gravityList = new ArrayList<Double>();
	protected ArrayList<Double> rotationList = new ArrayList<Double>();
	protected ArrayList<Double> temperatureList = new ArrayList<Double>();
	protected ArrayList<Double> radiusList = new ArrayList<Double>();
	protected ArrayList<Double> orbitalRadiusList = new ArrayList<Double>();
	protected ArrayList<Double> luminosityList = new ArrayList<Double>();
	protected ArrayList<Double> magFieldList = new ArrayList<Double>();
	
	
	protected ArrayList<String> names = new ArrayList<String>();
	
	//private Key ownerKey;	
	protected double hitPoints;
	
	protected ArrayList<IPhysicalDataModel> models = new ArrayList<IPhysicalDataModel>();
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#addPhysicalModel(model.PhysicalModel)
	 */
	public void addPhysicalModel(IPhysicalDataModel model) {
		this.models.add(model);
		model.setContainer(this);
		updateParameters();		
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#removePhysicalModel(model.IPhysicalModel)
	 */
	public void removePhysicalModel(IPhysicalDataModel model) {
		this.models.remove(model);
		model.setContainer(null);
		updateParameters();
	}
	/*
	 * Parameters that are part of inherent characteristics of models
	 */
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#updateParameters()
	 */
	public void updateParameters() {
		massList.clear();
		location.zero(); //set(0.0f, 0.0f, 0.0f);//
		velocity.zero();
		ageList.clear();
		rotationList.clear();
		temperatureList.clear();
		radiusList.clear();
		orbitalRadiusList.clear();
		luminosityList.clear();
		magFieldList.clear();
		
		double tempMass = 0.0;
		double tempAge = 0.0;
		double tempRotation = 0.0;
		double tempTemperature = 0.0;
		double tempRadius = 0.0;
		double tempOrbitalRadius = 0.0;
		double tempLuminosity = 0.0;
		double tempMagField = 0.0;
		for (IPhysicalDataModel model: models) {
			tempMass += model.getMass();
			massList.add(model.getMass());
			location.set(model.getLocation());
			velocity.set(model.getVelocity());
			tempAge += model.getAge();
			ageList.add(model.getAge());
			tempRotation += model.getRotation();
			rotationList.add(model.getRotation());
			tempTemperature += model.getTemperature();
			temperatureList.add(model.getTemperature());
			tempRadius += model.getRadius();
			radiusList.add(model.getRadius());
			tempOrbitalRadius += model.getOrbitalRadius();
			orbitalRadiusList.add(model.getOrbitalRadius());
			tempLuminosity += model.getLuminosity();
			luminosityList.add(model.getLuminosity());
			tempMagField += model.getMagField();
			magFieldList.add(model.getMagField());
		}
		
		this.mass = tempMass;
		this.radius = tempRadius;
		this.luminosity = tempLuminosity;
		this.magField = tempMagField;
		
		this.age = tempAge/models.size();
		this.rotation = tempRotation/models.size();
		this.temperature = tempTemperature/models.size();		
		this.orbitalRadius = tempOrbitalRadius/models.size();		
		
		calculateParameters();
	}
	
	/*
	 * Parameters that are calculated based on underlying characteristics of models
	 */
	private void calculateParameters() {
		volumeList.clear();
		densityList.clear();
		massEnergyList.clear();
		spinEnergyList.clear();
		radiationEnergyList.clear();
		gravEnergyList.clear();
		magneticEnergyList.clear();
		fullEnergyList.clear();
		gravityList.clear();		
		
		double tempVolume = 0.0;
		double cumuVolume = 0.0;
		double tempDensity = 0.0;
		double cumuDensity = 0.0;
		double tempMassEnergy = 0.0;
		double cumuMassEnergy = 0.0;
		double tempSpinEnergy = 0.0;
		double cumuSpinEnergy = 0.0;
		double tempRadiationEnergy = 0.0;
		double cumuRadiationEnergy = 0.0;
		double tempGravEnergy = 0.0;
		double cumuGravEnergy = 0.0;
		double tempMagneticEnergy = 0.0;
		double cumuMagneticEnergy = 0.0;
		double tempFullEnergy = 0.0;
		double cumuFullEnergy = 0.0;
		double tempGravity = 0.0;
		double cumuGravity = 0.0;
		for (int i = 0; i < models.size(); i++) {
			tempVolume = 4*Math.PI*radiusList.get(i)*radiusList.get(i)*radiusList.get(i)/3;
			cumuVolume += tempVolume;
			volumeList.add(tempVolume);
			
			tempGravity = Constants.GravitationalConstant*massList.get(i)/(radiusList.get(i)*1000*radiusList.get(i)*1000)/Constants.EarthGravity;
			cumuGravity += tempGravity;
			gravityList.add(tempGravity);
			
			tempDensity = massList.get(i)/volumeList.get(i);
			cumuDensity += tempDensity;
			densityList.add(tempDensity);
			
			tempMassEnergy = massList.get(i)*Constants.SpeedOfLight*Constants.SpeedOfLight;
			cumuMassEnergy += tempMassEnergy;
			massEnergyList.add(tempMassEnergy);
			
			tempSpinEnergy = 0.5 * (0.4*massList.get(i)*radiusList.get(i)*radiusList.get(i)) * 2*Math.PI*rotationList.get(i);
			cumuSpinEnergy += tempSpinEnergy;
			spinEnergyList.add(tempSpinEnergy);
			
			tempRadiationEnergy = luminosityList.get(i);
			cumuRadiationEnergy += tempRadiationEnergy;
			radiationEnergyList.add(tempRadiationEnergy);
			
			tempGravEnergy = gravityList.get(i)*gravityList.get(i)*Constants.EarthGravity*Constants.EarthGravity/
				(8*Math.PI*Constants.GravitationalConstant) * volumeList.get(i);
			cumuGravEnergy += tempGravEnergy;
			gravEnergyList.add(tempGravEnergy);
			
			tempMagneticEnergy = volumeList.get(i)*magFieldList.get(i)*magFieldList.get(i)*0.5/Constants.MagneticPermeability;
			cumuMagneticEnergy += tempMagneticEnergy;
			magneticEnergyList.add(tempMagneticEnergy);
			
			tempFullEnergy = tempMassEnergy + tempSpinEnergy + tempRadiationEnergy + tempGravEnergy + tempMagneticEnergy;
			cumuFullEnergy += tempFullEnergy;
			fullEnergyList.add(tempFullEnergy);
		}
		
		this.volume = cumuVolume;
		this.density = cumuDensity/models.size(); //average density
		this.massEnergy = cumuMassEnergy;
		this.spinEnergy = cumuSpinEnergy;
		this.radiationEnergy = cumuRadiationEnergy;
		this.gravEnergy = cumuGravEnergy;
		this.magneticEnergy = cumuMagneticEnergy;
		this.fullEnergy = cumuFullEnergy;
		this.gravity = Constants.GravitationalConstant*this.mass/(this.radius*1000*this.radius*1000)/Constants.EarthGravity;
		//update views
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getModels()
	 */
//	public ArrayList<Key> getModels() {
//		ArrayList<Key> keys = new ArrayList<Key>();
//		for (PhysicalModel model: models) {
//			keys.add(model.getKey());
//		}
//		return keys;
//	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getLocation()
	 */
	public Vector2f getLocation() {
		return this.location;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getVelocity()
	 */
	public Vector2f getVelocity() {
		return this.velocity;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalMass()
	 */
	public double getTotalMass() {
		return mass;
	}

	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalLuminosity()
	 */
	public double getTotalLuminosity() {
		return luminosity;
	}

	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getOrbitalRadius()
	 */
	public double getOrbitalRadius() {
		return orbitalRadius;
	}

	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalRadius()
	 */
	public double getTotalRadius() {
		return radius;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalVolume()
	 */
	public double getTotalVolume() {
		return this.volume;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getAverageDensity()
	 */
	public double getAverageDensity() {
		return this.density;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalFullEnergy()
	 */
	public double getTotalFullEnergy() {
		return this.fullEnergy;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalMassEnergy()
	 */
	public double getTotalMassEnergy() {
		return this.massEnergy;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalSpinEnergy()
	 */
	public double getTotalSpinEnergy() {
		return this.spinEnergy;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalRadiationEnergy()
	 */
	public double getTotalRadiationEnergy() {
		return this.radiationEnergy;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalGravEnergy()
	 */
	public double getTotalGravEnergy() {
		return this.gravEnergy;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalMagneticEnergy()
	 */
	public double getTotalMagneticEnergy() {
		return this.magneticEnergy;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getAverageAge()
	 */
	public double getAverageAge() {
		return this.age;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalGravity()
	 */
	public double getTotalGravity() {
		return this.gravity;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getAverageRotation()
	 */
	public double getAverageRotation() {
		return this.rotation;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getAverageTemperature()
	 */
	public double getAverageTemperature() {
		return this.temperature;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTotalMagField()
	 */
	public double getTotalMagField() {
		return this.magField;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getCurrentName()
	 */
	public String getCurrentName() {
		return this.names.get(names.size()-1);
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getMassList()
	 */
	public ArrayList<Double> getMassList() {
		return this.massList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getVolumeList()
	 */
	public ArrayList<Double> getVolumeList() {
		return this.volumeList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getDensityList()
	 */
	public ArrayList<Double> getDensityList() {
		return this.densityList;
	}	
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getFullEnergyList()
	 */
	public ArrayList<Double> getFullEnergyList() {
		return this.fullEnergyList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getMassEnergyList()
	 */
	public ArrayList<Double> getMassEnergyList() {
		return this.massEnergyList;
	}
		
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getSpinEnergyList()
	 */
	public ArrayList<Double> getSpinEnergyList() {
		return this.spinEnergyList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getRadiationEnergyList()
	 */
	public ArrayList<Double> getRadiationEnergyList() {
		return this.radiationEnergyList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getGravEnergyList()
	 */
	public ArrayList<Double> getGravEnergyList() {
		return this.gravEnergyList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getMagneticEnergyList()
	 */
	public ArrayList<Double> getMagneticEnergyList() {
		return this.magneticEnergyList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getAgeList()
	 */
	public ArrayList<Double> getAgeList() {
		return this.ageList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getGravityList()
	 */
	public ArrayList<Double> getGravityList() {
		return this.gravityList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getRotationList()
	 */
	public ArrayList<Double> getRotationList() {
		return this.rotationList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getTemperatureList()
	 */
	public ArrayList<Double> getTemperatureList() {
		return this.temperatureList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getRadiusList()
	 */
	public ArrayList<Double> getRadiusList() {
		return this.radiusList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getOrbitalRadiusList()
	 */
	public ArrayList<Double> getOrbitalRadiusList() {
		return this.orbitalRadiusList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getLuminosityList()
	 */
	public ArrayList<Double> getLuminosityList() {
		return this.luminosityList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getMagFieldList()
	 */
	public ArrayList<Double> getMagFieldList() {
		return this.magFieldList;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#getNameList()
	 */
	public ArrayList<String> getNameList() {
		return this.names;
	}
	
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#addName(java.lang.String)
	 */
	public void addName(String name) {
		this.names.add(name);
		//update views
	}
	/* (non-Javadoc)
	 * @see model.IPhysicalContainerModel#removeName(java.lang.String)
	 */
	public void removeName(String name) {
		this.names.remove(name);
		//update views
	}
	
//	public void addMass(Double mass) {
//	this.massList.add(mass);
//	this.mass += mass;
//}
//
//public void addLuminosity(Double luminosity) {
//	this.luminosityList.add(luminosity);
//	this.luminosity += luminosity;
//}
//
//public void addOrbitalRadius(Double orbitalRadius) {
//	this.orbitalRadiusList.set(0, orbitalRadius);
//	this.orbitalRadius = orbitalRadius;
//}
//
//public void addRadius(Double radius) {
//	this.radiusList.add(radius);
//	this.radius += (2*radius);
//}

//Continue...
	
}
